import model
import datetime
import httplib2
import resource.string.app

from _console import Console

from progressbar import (Bar, ETA, Percentage, ProgressBar, SimpleProgress)


class Updater(Console):
    """
    Update post from booru.
    """

    session = None
    """
    The database connection.
    """

    def run(self):
        """
        Main program.

        @return: None.
        @rtype: None
        """

        self.greet(resource.string.app)
        self.session = self.open_db()
        # todo: Remove this line before deploying.
        # self.reset_db()
        self.update()
        self.farewell()

    def reset_db(self):
        """
        Reset db. Used during development only.

        @return: None.
        @rtype: None
        """

        self.session.query(model.Image).delete()
        self.session.query(model.Tag).delete()
        self.session.query(model.ImageTag).delete()
        self.session.query(model.SearchHost).delete()

    def update(self):
        """
        Update post from booru.

        @return: None.
        @rtype: None
        """

        stopped = False
        updating = 'Updating search criteria "%s" page %d from %s... '
        abort = 'Keyboard interrupt detected. Operation aborted.\n'
        wait = 'Standby for 1 second.\n'
        up_to_date = 'Search criteria "%s" from %s is up-to-date.\n'
        cool_down = 'Update failed. Cooling down for 5 seconds.\n'

        searches = self.session.query(model.Search)
        hosts = self.load_hosts()
        for search in searches:
            for host in hosts:
                new = False
                search_host = self.load_search_host(search, host)
                if not search_host:
                    new = True
                    search_host = self.create_search_host(search, host)

                if not (search_host.should_refresh or new):
                    t = (search.value, host.name)
                    self.yell(up_to_date % t)
                    continue

                while not stopped:
                    t = (search.value, search_host.last_page, host.name)
                    self.progress(updating % t)
                    succeed = False
                    headers = {}

                    # todo: Remove these following 3 lines before deploying.
                    # mock_data = 'otomari_honey'
                    # mock_data = 'carnelian'
                    # headers, posts = search_host.mock_download(mock_data)

                    try:
                        # todo: Uncomment this line before deploying.
                        headers, posts = search_host.download()
                        succeed = True
                    except KeyboardInterrupt:
                        stopped = True
                        self.warn('Canceled ~_~\n')
                        self.flunk(abort)
                    except httplib2.HTTPConnectionWithTimeout:
                        self.flunk('Connection time out >_<\n')
                        self.yell(cool_down)
                        stopped = Console.sleep(5)

                    if succeed:
                        self.cheer('OK ^_^\n')
                        if headers['status'] != 200 or not posts:
                            break

                        quantity = len(posts)

                        try:
                            for post in posts:
                                if not post:
                                    continue

                                image = self.load_image(post)
                                if not image:
                                    image = self.create_image(post)
                                    image.save_host(post)
                                image.save_tags(post.tags)

                            if quantity == 100:
                                search_host.last_page += 1
                            else:
                                now = datetime.datetime.now()
                                search_host.last_update = now

                            if self.session.new or self.session.dirty:
                                self.session.commit()

                            self.yell(wait)
                            stopped = Console.sleep(1)

                            if quantity < 100:
                                break

                        except KeyboardInterrupt:
                            self.flunk(abort)
                            stopped = True

                    # todo: Remove this line before deploying.
                    # stopped = True

                # todo: Remove this line before deploying.
                # stopped = True

                if stopped:
                    break

            if stopped:
                break

    def load_hosts(self):
        """
        Load host model object.

        @return: List of Host model object filtered by active status.
        @rtype: list
        """

        hosts = self.session.query(model.Host)
        return hosts.filter(model.Host.status == model.Host.STATUS_ACTIVE)

    def load_search_host(self, search, host):
        """
        Load SearchHost model object of search and host.

        @type search: Search
        @param search: Search model instance which id to be searched.
        @type host: Host
        @param host: Host model instance which id to be searched.

        @rtype: SearchHost or None
        @return: SearchHost model instance or None.
        """

        search_id = model.SearchHost.search_id
        host_id = model.SearchHost.host_id
        query = self.session.query(model.SearchHost)
        query = query.filter(search_id == search.id)
        query = query.filter(host_id == host.id)
        return query.first()

    def create_search_host(self, search, host):
        """
        Create SearchHost model of search and host.

        @type search: Search
        @param search: Search model instance
        @type host: Host
        @param host: Host model instance

        @rtype: SearchHost
        @return: SearchHost model instance.
        """

        search_host = model.SearchHost()
        search_host.search = search
        search_host.host = host
        search_host.last_page = 1

        self.session.add(search_host)
        self.session.commit()

        return search_host

    @staticmethod
    def create_save_gauge(maximum):
        """
        Create new save gauge.

        @type maximum: int
        @param maximum: Maximum value of gauge.

        @rtype: ProgressBar
        @return: ProgressBar instance.
        """

        widgets = ['Processing ', SimpleProgress(), ' ',
                   Bar(left='[', right=']'), ' ', Percentage(), ' ', ETA()]
        gauge = ProgressBar(widgets=widgets, maxval=maximum)
        gauge.start()
        return gauge

    def load_image(self, post):
        """
        Load Image object of post by MD5.

        @param post: Post object to be search.
        @type post: model.Post

        @return: Image object or None.
        @rtype: model.Image or None
        """

        query = self.session.query(model.Image)
        return query.filter(model.Image.md5 == post.md5).first()

    def create_image(self, post):
        """
        Create Image model from post data.

        @type post: model.Post
        @param post: A Post object.

        @rtype: model.Image
        @return: Image model instance.
        """

        image = model.Image(post)
        self.session.add(image)
        self.session.commit()

        return image
